/*
* Copyright (c) 2001 Sun Microsystems, Inc.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*       Sun Microsystems, Inc. for Project JXTA."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Sun", "Sun Microsystems, Inc.", "RosettaChat", "JXTA" and
*    "Project JXTA" must not be used to endorse or promote products derived
*    from this software without prior written permission. For written
*    permission, please contact Project JXTA at http://www.jxta.org.
*
* 5. Products derived from this software may not be called "JXTA",
*    nor may "JXTA" appear in their name, without prior written
*    permission of Sun.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL SUN MICROSYSTEMS OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of Project JXTA.  For more
* information on Project JXTA, please see
* <http://www.jxta.org/>.
*
* This license is based on the BSD license adopted by the Apache Foundation.
*/

package net.jxta.myjxta.misc.rosettachat.http;

import net.jxta.myjxta.misc.rosettachat.misc.EmptyIterator;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * GetMessage.java
 *
 * @author James Todd [jwtodd@pacbell.net]
 */

public class GetMessage {

    protected String method = null;
    protected Message message = null;
    protected URLConnection connection = null;

    private static final String PROVIDER =
            "com.sun.net.ssl.internal.ssl.Provider";
    private static boolean VERBOSE = false;
    private URL url = null;
    private boolean isUnicodeEncoding = false;
    private boolean isFollowRedirect = false;

    static {
        try {
            Class clazz = Class.forName(PROVIDER);

            if (Provider.class.isAssignableFrom(clazz)) {
                Provider provider = (Provider) clazz.newInstance();

                Security.addProvider(provider);
            }
        } catch (ClassNotFoundException cnfe) {
            if (VERBOSE) {
                cnfe.printStackTrace();
            }
        } catch (IllegalAccessException iae) {
            if (VERBOSE) {
                iae.printStackTrace();
            }
        } catch (InstantiationException ie) {
            if (VERBOSE) {
                ie.printStackTrace();
            }
        } catch (SecurityException se) {
            if (VERBOSE) {
                se.printStackTrace();
            }
        }
    }

    public GetMessage() {
        this(null, null);
    }

    public GetMessage(URL url) {
        this(url, null);
    }

    public GetMessage(URL url, Message message) {
        this.url = url;
        this.method = Constants.HTTP.GET;
        this.message = message;
    }

    public URL getURL() {
        return this.url;
    }

    public void setURL(URL url) {
        this.url = url;
    }

    public String getHeader(String key) {
        return ((this.message != null) ?
                (String) this.message.getHeader(key) : null);
    }

    public Iterator getHeaderKeys() {
        return ((this.message != null) ?
                this.message.getHeaderKeys() : new EmptyIterator());
    }

    public void setHeaders(Map headers) {
        Iterator keys = headers.keySet().iterator();

        while (keys.hasNext()) {
            String key = (String) keys.next();
            String value = (String) headers.get(key);

            setHeader(key, value);
        }
    }

    public void setHeader(String key, String value) {
        if (this.message == null) {
            this.message = new Message();
        }

        this.message.setHeader(key, value);
    }

    public void removeHeader(String key)
            throws NullPointerException {
        if (key == null) {
            throw new NullPointerException("null key");
        }

        if (this.message != null) {
            this.message.removeHeader(key);
        }
    }

    public boolean isUnicodeEncoding() {
        return this.isUnicodeEncoding;
    }

    public void setUnicodeEncoding(boolean isUnicodeEncoding) {
        this.isUnicodeEncoding = isUnicodeEncoding;
    }

    public boolean isFollowRedirect() {
        return this.isFollowRedirect;
    }

    public void setFollowRedirect(boolean isFollowRedirect) {
        this.isFollowRedirect = isFollowRedirect;
    }

    public Message dispatch()
            throws IOException {
        openConnection();

        try {
            doGet();
        } catch (IOException ioe) {
            throw new IOException(ioe.getMessage());
        }

        Message response = getResponse();

        closeConnection();

        return response;
    }

    public String toString() {
        java.lang.Class<? extends GetMessage> clazz = getClass();
        java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
        java.util.HashMap<String, Object> map = new java.util.HashMap<String, Object>();
        java.lang.String object = null;
        java.lang.Object value = null;

        for (int i = 0; i < fields.length; i++) {
            try {
                object = fields[i].getName();
                value = fields[i].get(this);

                map.put(object, (value != null) ? value : "null");
            } catch (IllegalAccessException iae) {
                iae.printStackTrace();
            }
        }

        if (clazz.getSuperclass().getSuperclass() != null) {
            map.put("super", clazz.getSuperclass().toString());
        }

        return clazz.getName() + map;
    }

    protected String getBody() {
        return ((this.message != null &&
                this.message.hasBody()) ?
                (String) this.message.getBody() : null);
    }

    protected void setBody(String body) {
        if (this.message == null) {
            this.message = new Message();
        }

        this.message.setBody((body != null) ? body : "");
    }

    protected void openConnection()
            throws IllegalStateException, IOException {
        if (this.url == null) {
            throw new IllegalStateException("null url");
        }

        if (this.url.getProtocol().equalsIgnoreCase("http") ||
                this.url.getProtocol().equalsIgnoreCase("https")) {
            openHTTPConnection();
        } else {
            openFileConnection();
        }
    }

    protected void closeConnection() {
        if (this.connection instanceof HttpURLConnection) {
            ((HttpURLConnection) this.connection).disconnect();
        }

        this.connection = null;
    }

    protected void doGet()
            throws IOException {
    }

    protected Message getResponse() throws IOException {
        Map<String, String> headers = getResponseHeaders();
        String body = "";

        body = getResponseBody();

        return new Message(headers, body);
    }

    private void openHTTPConnection()
            throws IOException {
        this.connection = (HttpURLConnection) this.url.openConnection();

        ((HttpURLConnection) this.connection).setRequestMethod(this.method);

        if (this.message != null) {
            Iterator keys = this.message.getHeaderKeys();

            while (keys.hasNext()) {
                String key = (String) keys.next();
                String value = (String) this.message.getHeader(key);

                this.connection.setRequestProperty(key, value);
            }
        }

        boolean doOutput = (this.method.equals(Constants.HTTP.POST) &&
                this.message != null &&
                this.message.hasBody());
        Map defaultHeaders = ((!doOutput) ?
                Util.getDefaultGetHeaders() :
                Util.getDefaultPostHeaders());
        Iterator defaultHeaderKeys = defaultHeaders.keySet().iterator();

        while (defaultHeaderKeys.hasNext()) {
            String key = (String) defaultHeaderKeys.next();

            if (this.connection.getRequestProperty(key) == null) {
                String value = (String) defaultHeaders.get(key);

                this.connection.setRequestProperty(key, value);

            }
        }

        if (doOutput) {
            int l = this.message.getBody().toCharArray().length;

            this.connection.setRequestProperty(Constants.MIME.Key.CONTENT_LENGTH,
                    Integer.toString(l));
        }

        this.connection.setDoOutput(doOutput);
        this.connection.setDoInput(true);
        this.connection.setUseCaches(false);
        HttpURLConnection.setFollowRedirects(this.isFollowRedirect);
    }

    private void openFileConnection()
            throws IOException {
        this.connection = this.url.openConnection();
    }

    private Map<String, String> getResponseHeaders() {
        Map<String, String> headers = null;

        // xxx
        // required as java.net.HttpURLConnection.getHeaderFieldKey(int)
        // appears to prematurely return a null - bummer

        //for (int i = 0; true; i++) {
        for (int i = 0; i < Constants.HTTP.MAX_HEADERS; i++) {
            String key = this.connection.getHeaderFieldKey(i);

            if (key != null) {
                String value = this.connection.getHeaderField(key);

                if (headers == null) {
                    headers = new HashMap<String, String>();
                }

                headers.put(key, value);
            }
        }

        return headers;
    }

    private String getResponseBody()
            throws IOException {
        final int BLOCK = 1024;
        InputStream is = this.connection.getInputStream();
        String contentType = this.connection.getContentType();
        InputStreamReader isr =
                ((this.isUnicodeEncoding) ?
                        new InputStreamReader(is, Util.getCharSet(contentType)) :
                        new InputStreamReader(is));
        BufferedReader reader = new BufferedReader(isr);
        char[] buf = new char[1024];
        int l = 0;
        //int contentLength = this.connection.getContentLength();
        CharArrayWriter writer = new CharArrayWriter();

        while ((l = reader.read(buf, 0, BLOCK)) > -1) {
            writer.write(buf, 0, l);
        }

        reader.close();

        return ((this.isUnicodeEncoding) ?
                Util.toUnicodeEncoded(new String(writer.toCharArray())) :
                new String(writer.toCharArray()));
    }
}
